﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

using Mono.XNA.Scene;

namespace Mono.XNA.Render
{
    public class Camera
    {
        public Matrix ViewMatrix
        {
            get { return viewMatrix; }
        }
        private Matrix viewMatrix;

        public Matrix ProjectionMatrix
        {
            get { return projectionMatrix; }
        }
        private Matrix projectionMatrix;

        public Vector3 Position
        {
            get { return position; }
            set 
            { 
                this.position = value;
                Update();
            }
        }
        private Vector3 position;

        public Vector3 Rotation
        {
            get { return GetRotationVector(rotationMatrix); }
            set
            {
                this.rotationMatrix = GetRotationMatrix(value);
                Update();
            }
        }
        private Matrix rotationMatrix = Matrix.Identity;

        public Vector3 Up
        {
            set { up = value; }
            private get { return up; }
        }
        private Vector3 up;


        public Camera()
        {
            Initialize();
        }

        private void Initialize()
        {
            position = Vector3.Zero;
        }

        private Matrix GetRotationMatrix(Vector3 rotation)
        {
            Matrix rot = Matrix.Identity;
            rot *= Matrix.CreateRotationX(rotation.X);
            rot *= Matrix.CreateRotationY(rotation.Y);
            rot *= Matrix.CreateRotationZ(rotation.Z);
            return rot;
        }
        private Vector3 GetRotationVector(Matrix matrix)
        {
            // y, z軸取得
            Vector3 y_axis = new Vector3(matrix.M12, matrix.M22, matrix.M32);
            Vector3 z_axis = new Vector3(matrix.M13, matrix.M23, matrix.M33);

            // 回転角取得
            Vector3 rotation = Vector3.Zero;
            // ｙ回転角
            rotation.Y = (float)Math.Atan2((double)z_axis.X, (double)z_axis.Z);

            // ｘ回転角
            float cos_y = (float)Math.Cos((double)rotation.Y);
            rotation.X = (float)Math.Atan2((double)(cos_y * z_axis.Y), Math.Abs(z_axis.Z));

            // ｚ回転角
            float sin_y = (float)Math.Sin(rotation.Y);
            float cos_x = (float)Math.Cos(rotation.X);
            rotation.Z = (float)Math.Atan2(cos_x * (sin_y * y_axis.Z - cos_y * y_axis.X), y_axis.Y);

            return rotation;
        }

        private void Update()
        {
            SetLookAt(position, Rotation, Up);
        }

        /// <summary>
        /// カメラの向きを調整する
        /// </summary>
        /// <param name="inEye">カメラ位置</param>
        /// <param name="target">注視点</param>
        /// <param name="up">カメラの上方向のベクトル</param>
        public void SetLookAt(Vector3 inEye, Vector3 target, Vector3 up)
        {
            this.position = inEye;
            this.rotationMatrix = GetRotationMatrix(target);
            this.up = up;

            this.viewMatrix = Matrix.CreateLookAt(inEye, target, up);
        }
        /// <summary>
        /// 射影行列作成
        /// </summary>
        /// <param name="fieldOfView">Y方向の視野角</param>
        /// <param name="aspect">画面のアスペクト比</param>
        /// <param name="near">ニアクリップ面までの距離</param>
        /// <param name="far">ファークリップ面までの距離</param>
        public void SetPerspectiveFieldOfView(float fieldOfView, float near, float far)
        {
            this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
                        MathHelper.ToRadians(fieldOfView),
                        (float)SceneManager.GameObject.GraphicsDevice.Viewport.Width /
                            (float)SceneManager.GameObject.GraphicsDevice.Viewport.Height,
                        near,
                        far
                    );
        }
    }
}
